﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using McTools.Xrm.Connection;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;
using SiteMapEditor.AppCode;
using SiteMapEditor.Controls;
using SiteMapEditor.Forms;
using Tanguy.WinForm.Utilities.DelegatesHelpers;

namespace SiteMapEditor
{
    public partial class MainForm : Form
    {
        #region Variables

        /// <summary>
        /// Crm Organization Service
        /// </summary>
        internal static IOrganizationService service;
        internal static List<EntityMetadata> entityCache;
        internal static List<Entity> webResourcesHtmlCache;
        internal static List<Entity> webResourcesImageCache;

        CrmConnectionStatusBar ccsb;

        ConnectionManager cManager;

        XmlDocument siteMapDoc;

        Entity siteMap;

        internal SiteMapEditor.AppCode.Clipboard clipboard = new AppCode.Clipboard();

        #endregion

        #region Constructors

        public MainForm()
        {
            try
            {
                InitializeComponent();

                this.Text += string.Format(" ({0})",
                                           Assembly.GetExecutingAssembly().GetName().Version);

                this.cManager = new ConnectionManager(this);
                this.cManager.ConnectionSucceed += new ConnectionManager.ConnectionSucceedEventHandler(cManager_ConnectionSucceed);
                this.cManager.ConnectionFailed += new ConnectionManager.ConnectionFailedEventHandler(cManager_ConnectionFailed);
                this.cManager.StepChanged += new ConnectionManager.StepChangedEventHandler(cManager_StepChanged);

                ccsb = new CrmConnectionStatusBar(this.cManager);
                this.Controls.Add(ccsb);
            }
            catch (Exception error)
            {
                MessageBox.Show(this, "Error while initializing this application: " + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        void cManager_StepChanged(object sender, StepChangedEventArgs e)
        {
            this.ccsb.SetMessage(e.CurrentStep);
        }

        void cManager_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            this.ccsb.SetMessage("Error: " + e.FailureReason);
            MessageBox.Show("Error: " + e.FailureReason, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            service = null;
        }

        void cManager_ConnectionSucceed(object sender, ConnectionSucceedEventArgs e)
        {
            ccsb.RebuildConnectionList();

            service = e.OrganizationService;

            this.ccsb.SetConnectionStatus(true, e.ConnectionDetail);
            this.ccsb.SetMessage(string.Empty);

            if (e.Parameter != null)
            {
                if (e.Parameter.ToString() == "loadSiteMap")
                {
                    this.LoadSiteMap();
                }
                else if (e.Parameter.ToString() == "updateSiteMap")
                {
                    this.UpdateSiteMap();
                }
            }
        }

        #endregion

        #region Methods

        #region Main ToolStrip Menu

        private void tsbMainOpenSiteMap_Click(object sender, EventArgs e)
        {
            if (service == null)
                this.cManager.AskForConnection("loadSiteMap");
            else
            {
                this.LoadSiteMap();
            }
        }

        private void tsbMainImport_Click(object sender, EventArgs e)
        {
            if (service == null)
            {
                this.cManager.AskForConnection("updateSiteMap");
            }
            else
            {
                this.UpdateSiteMap();
            }
        }

        private void toolStripButtonLoadSiteMapFromDisk_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog()
            {
                Title = "Select a Xml file representing a SiteMap",
                Filter = "Xml file (*.xml)|*.xml"
            };

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                this.EnableControls(false);

                this.siteMapDoc = new XmlDocument();
                this.siteMapDoc.Load(ofd.FileName);

                if (this.siteMapDoc.DocumentElement.Name != "SiteMap" || this.siteMapDoc.DocumentElement.ChildNodes.Count > 0 && this.siteMapDoc.DocumentElement.ChildNodes[0].Name == "SiteMap")
                {
                    MessageBox.Show(this, "Invalid Xml: SiteMap Xml root must be SiteMap!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    this.tsbMainOpenSiteMap.Enabled = true;
                    this.toolStripButtonLoadSiteMapFromDisk.Enabled = true;
                }
                else
                {
                    this.DisplaySiteMap();
                    this.EnableControls(true);
                }
            }
        }

        private void toolStripButtonSaveSiteMapToDisk_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog()
            {
                Title = "Select a location to save the SiteMap as a Xml file",
                Filter = "Xml file (*.xml)|*.xml",
                FileName = "SiteMap.xml"
            };

            if (sfd.ShowDialog() == DialogResult.OK)
            {
                this.EnableControls(false);

                // Build the Xml SiteMap from SiteMap TreeView
                XmlDocument doc = new XmlDocument();
                XmlNode rootNode = doc.CreateElement("SiteMap");
                doc.AppendChild(rootNode);

                this.AddXmlNode(this.tvSiteMap.Nodes[0], rootNode);

                if (this.siteMap != null)
                {
                    this.siteMap["sitemapxml"] = doc.SelectSingleNode("SiteMap/SiteMap").OuterXml;
                }

                this.siteMapDoc.LoadXml(doc.SelectSingleNode("SiteMap/SiteMap").OuterXml);

                this.siteMapDoc.Save(sfd.FileName);

                this.EnableControls(true);

                MessageBox.Show(this, "SiteMap saved!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void resetSiteMapToDefaultToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Assembly myAssembly = Assembly.GetExecutingAssembly();
            using (StreamReader reader = new StreamReader(myAssembly.GetManifestResourceStream("SiteMapEditor.Resources.sitemap.xml")))
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(reader.ReadToEnd());

                this.siteMapDoc = new XmlDocument();
                this.siteMapDoc.LoadXml(doc.SelectSingleNode("ImportExportXml/SiteMap/SiteMap").OuterXml);
            }

            DisplaySiteMap();
        }

        private void tsbAbout_Click(object sender, EventArgs e)
        {
            // Displays about form
            AboutForm form = new AboutForm();
            form.StartPosition = FormStartPosition.CenterParent;
            form.ShowDialog();
        }

        #endregion Main ToolStrip Menu

        #region TreeView ToolStrip Menu

        private void toolStripButtonMoveDown_Click(object sender, EventArgs e)
        {
            this.toolStripButtonMoveDown.Enabled = false;

            TreeNode tnmNode = tvSiteMap.SelectedNode;
            TreeNode tnmNextNode = tnmNode.NextNode;

            if (tnmNextNode != null)
            {
                int idxBegin = tnmNode.Index;
                int idxEnd = tnmNextNode.Index;
                TreeNode tnmNodeParent = tnmNode.Parent;
                if (tnmNodeParent != null)
                {
                    tnmNode.Remove();
                    tnmNextNode.Remove();

                    tnmNodeParent.Nodes.Insert(idxBegin, tnmNextNode);
                    tnmNodeParent.Nodes.Insert(idxEnd, tnmNode);

                    this.tvSiteMap.SelectedNode = tnmNode;
                }
            }

            this.toolStripButtonMoveDown.Enabled = true;
        }

        private void toolStripButtonMoveUp_Click(object sender, EventArgs e)
        {
            this.toolStripButtonMoveUp.Enabled = false;

            TreeNode tnmNode = tvSiteMap.SelectedNode;
            TreeNode tnmPreviousNode = tnmNode.PrevNode;

            if (tnmPreviousNode != null)
            {
                int idxBegin = tnmNode.Index;
                int idxEnd = tnmPreviousNode.Index;
                TreeNode tnmNodeParent = tnmNode.Parent;
                if (tnmNodeParent != null)
                {
                    tnmNode.Remove();
                    tnmPreviousNode.Remove();

                    tnmNodeParent.Nodes.Insert(idxEnd, tnmNode);
                    tnmNodeParent.Nodes.Insert(idxBegin, tnmPreviousNode);

                    this.tvSiteMap.SelectedNode = tnmNode;
                }
            }
        
            this.toolStripButtonMoveUp.Enabled = true;
        }

        private void toolStripButtonDelete_Click(object sender, EventArgs e)
        {
            this.tvSiteMap.SelectedNode.Remove();
        }

        private void toolStripButtonDisplayXml_Click(object sender, EventArgs e)
        {
            TreeNode selectedNode = this.tvSiteMap.SelectedNode;
            Dictionary<string, string> collec = (Dictionary<string, string>)selectedNode.Tag;

            XmlDocument doc = new XmlDocument();
            doc.AppendChild(doc.CreateElement(selectedNode.Text.Split(' ')[0]));

            foreach (string key in collec.Keys)
            {
                XmlAttribute attr = doc.CreateAttribute(key);
                attr.Value = collec[key];

                doc.DocumentElement.Attributes.Append(attr);
            }

            foreach (TreeNode node in selectedNode.Nodes)
            {
                AddXmlNode(node, doc.DocumentElement);
            }

            XmlContentDisplayDialog xcdDialog = new XmlContentDisplayDialog(doc.OuterXml);
            xcdDialog.StartPosition = FormStartPosition.CenterParent;
            xcdDialog.ShowDialog();
        }

        private void toolStripButtonAddXml_Click(object sender, EventArgs e)
        {
            try
            {
                TreeNode selectedNode = this.tvSiteMap.SelectedNode;

                AddXmlForm axForm = new AddXmlForm();
                axForm.StartPosition = FormStartPosition.CenterParent;

                if (axForm.ShowDialog() == DialogResult.OK)
                {
                    XmlNode resultNode = axForm.AddedXmlNode;

                    switch (resultNode.Name)
                    {
                        case "Area":
                            {
                                if (!selectedNode.Text.StartsWith("SiteMap"))
                                {
                                    throw new Exception("Invalid Xml content for SiteMap node!\r\n\r\n'Area' Xml content is allowed only for 'SiteMap'.");
                                }
                            }
                            break;
                        case "Group":
                            {
                                if (!selectedNode.Text.StartsWith("Area"))
                                {
                                    throw new Exception("Invalid Xml content for Area node!\r\n\r\n'Group' Xml content is allowed only for 'Area'.");
                                }
                            }
                            break;
                        case "SubArea":
                            {
                                if (!selectedNode.Text.StartsWith("Group"))
                                {
                                    throw new Exception("Invalid Xml content for Group node!\r\n\r\n'SubArea' Xml content is allowed only for 'Group'.");
                                }
                            }
                            break;
                        case "Titles":
                            {
                                if (!selectedNode.Text.StartsWith("Group") && !selectedNode.Text.StartsWith("SubArea") && !selectedNode.Text.StartsWith("Area"))
                                {
                                    throw new Exception("Invalid Xml content for " + selectedNode.Text.Split(' ')[0] + " node!\r\n\r\n'Titles' Xml content is allowed only for 'Area', 'Group' and 'SubArea'.");
                                }
                            }
                            break;
                        case "Descriptions":
                            {
                                if (!selectedNode.Text.StartsWith("Group") && !selectedNode.Text.StartsWith("SubArea") && !selectedNode.Text.StartsWith("Area"))
                                {
                                    throw new Exception("Invalid Xml content for " + selectedNode.Text.Split(' ')[0] + " node!\r\n\r\n'Descriptions' Xml content is allowed only for 'Area', 'Group' and 'SubArea'.");
                                }
                            }
                            break;
                        case "Title":
                            {
                                if (!selectedNode.Text.StartsWith("Titles"))
                                {
                                    throw new Exception("Invalid Xml content for Titles node!\r\n\r\n'Title' Xml content is allowed only for 'Titles'.");
                                }
                            }
                            break;
                        case "Description":
                            {
                                if (!selectedNode.Text.StartsWith("Descriptions"))
                                {
                                    throw new Exception("Invalid Xml content for Descriptions node!\r\n\r\n'Description' Xml content is allowed only for 'Descriptions'.");
                                }
                            }
                            break;
                        case "Privilege":
                            {
                                if (!selectedNode.Text.StartsWith("SubArea"))
                                {
                                    throw new Exception("Invalid Xml content for SubArea node!\r\n\r\n'Privilege' Xml content is allowed only for 'SubArea'.");
                                }
                            }
                            break;
                        default:
                            throw new Exception("Unsupported Xml content!");
                    }

                    TreeNodeHelper.AddTreeViewNode(selectedNode, resultNode, this);
                }
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion TreeView ToolStrip Menu

        #region TreeView Handlers

        private void tvSiteMap_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            TreeNode selectedNode = e.Node;
            selectedNode.TreeView.SelectedNode = selectedNode;
            Dictionary<string, string> collec = (Dictionary<string, string>)selectedNode.Tag;

            TreeNodeHelper.AddContextMenu(e.Node, this);

            this.panelContainer.Controls.Clear();

            switch (selectedNode.Text.Split(' ')[0])
            {
                case "SiteMap":
                    {
                        SiteMapControl ctrl = new SiteMapControl(collec);
                        ctrl.Saved += new SiteMapControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "Area":
                    {
                        if(collec.Count == 0)collec.Add("Id", string.Format("tempId_{0}",DateTime.Now.Ticks));
                        AreaControl ctrl = new AreaControl(collec);
                        ctrl.Saved += new AreaControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "SubArea":
                    {
                        if (collec.Count == 0) collec.Add("Id", string.Format("tempId_{0}", DateTime.Now.Ticks));
                        SubAreaControl ctrl = new SubAreaControl(collec);
                        ctrl.Saved += new SubAreaControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "Group":
                    {
                        if (collec.Count == 0) collec.Add("Id", string.Format("tempId_{0}", DateTime.Now.Ticks));
                        GroupControl ctrl = new GroupControl(collec);
                        ctrl.Saved += new GroupControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "Privilege":
                    {
                        PrivilegeControl ctrl = new PrivilegeControl(collec);
                        ctrl.Saved += new PrivilegeControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "Description":
                    {
                        DescriptionControl ctrl = new DescriptionControl(collec);
                        ctrl.Saved += new DescriptionControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                case "Title":
                    {
                        TitleControl ctrl = new TitleControl(collec);
                        ctrl.Saved += new TitleControl.SaveEventHandler(ctrl_Saved);

                        this.panelContainer.Controls.Add(ctrl);
                        this.tsbItemSave.Visible = true;
                    }
                    break;
                default:
                    {
                        this.tsbItemSave.Visible = false;
                    }
                    break;
            }

            this.ManageMenuDisplay();
        }

        private void tvSiteMap_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNodeMouseClickEventArgs e2 = new TreeNodeMouseClickEventArgs(e.Node, MouseButtons.Left, 1, 0, 0);

            this.tvSiteMap_NodeMouseClick(this.tvSiteMap, e2);
        }

        private void tvSiteMap_KeyDown(object sender, KeyEventArgs e)
        {
            if (this.tvSiteMap.SelectedNode.Text != "SiteMap")
            {
                // Cut
                if (e.Control && e.KeyCode.ToString() == "X")
                    this.clipboard.Cut(this.tvSiteMap.SelectedNode);

                // Copy
                if (e.Control && e.KeyCode.ToString() == "C")
                    this.clipboard.Copy(this.tvSiteMap.SelectedNode);

                // Delete
                if (e.Control && e.KeyCode.ToString() == "D")
                    if (this.tvSiteMap.SelectedNode != null && this.tvSiteMap.SelectedNode.Text != "SiteMap")
                        this.toolStripButtonDelete_Click(null, null);

                // Move Up
                if (e.Control && e.KeyCode == Keys.Up)
                    if (this.tvSiteMap.SelectedNode != null && this.tvSiteMap.SelectedNode.Parent != null && this.tvSiteMap.SelectedNode.Index != 0)
                        this.toolStripButtonMoveUp_Click(null, null);

                // Move Down
                if (e.Control && e.KeyCode == Keys.Down)
                    if (this.tvSiteMap.SelectedNode != null && this.tvSiteMap.SelectedNode.Parent != null && this.tvSiteMap.SelectedNode.Index != this.tvSiteMap.SelectedNode.Parent.Nodes.Count - 1)
                        this.toolStripButtonMoveDown_Click(null, null);
            }

            // Paste
            if (e.Control && e.KeyCode.ToString() == "V")
                this.clipboard.Paste(this.tvSiteMap.SelectedNode);
        }

        #endregion TreeView Handlers

        #region SiteMap Component Handlers

        private void tsbItemSave_Click(object sender, EventArgs e)
        {
            ((ISiteMapSavable)this.panelContainer.Controls[0]).Save();

            Dictionary<string, string> nodeAttributesCollection = (Dictionary<string, string>)this.tvSiteMap.SelectedNode.Tag;

            if (nodeAttributesCollection.ContainsKey("Id"))
            {
                if (this.tvSiteMap.SelectedNode.Text.Split(' ').Length == 1)
                    this.tvSiteMap.SelectedNode.Text += " (" + ((Dictionary<string, string>)this.tvSiteMap.SelectedNode.Tag)["Id"] + ")";
                else
                    this.tvSiteMap.SelectedNode.Text = this.tvSiteMap.SelectedNode.Text.Split(' ')[0] + " (" + ((Dictionary<string, string>)this.tvSiteMap.SelectedNode.Tag)["Id"] + ")";

                this.tvSiteMap.SelectedNode.Name = this.tvSiteMap.SelectedNode.Text.Replace(" ", "");
            }

            if (nodeAttributesCollection.ContainsKey("LCID"))
            {
                this.tvSiteMap.SelectedNode.Text = this.tvSiteMap.SelectedNode.Text.Split(' ')[0] + " (" + ((Dictionary<string, string>)this.tvSiteMap.SelectedNode.Tag)["LCID"] + ")";

                this.tvSiteMap.SelectedNode.Name = this.tvSiteMap.SelectedNode.Text.Replace(" ", "");
            }
        }

        #endregion SiteMap Component Handlers

        #region ContextMenu Handlers

        private void nodeMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Name.Contains("System"))
            {
                string nodeText = e.ClickedItem.Name.Remove(0, 9);
                nodeText = nodeText.Substring(0, nodeText.IndexOf("ToolStripMenuItem"));

                SiteMapComponentPicker smcPicker = new SiteMapComponentPicker(nodeText);
                smcPicker.StartPosition = FormStartPosition.CenterParent;

                if (smcPicker.ShowDialog() == DialogResult.OK)
                {
                    Dictionary<string, string> collec = new Dictionary<string, string>();

                    foreach (XmlAttribute attr in smcPicker.SelectedNode.Attributes)
                    {
                        collec.Add(attr.Name, attr.Value);
                    }

                    string newNodeText = smcPicker.SelectedNode.Name + " (" + collec["Id"] + ")";

                    if (this.tvSiteMap.SelectedNode.Nodes.Find(newNodeText.Replace(" ", ""), false).Length > 0)
                    {
                        MessageBox.Show(this, "The selected tree node is already present!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        TreeNode newNode = new TreeNode(newNodeText);
                        newNode.Tag = collec;
                        newNode.Name = newNodeText.Replace(" ", "");

                        foreach (XmlNode childNode in smcPicker.SelectedNode.ChildNodes)
                        {
                            TreeNodeHelper.AddTreeViewNode(newNode, childNode, this);
                        }

                        TreeNodeMouseClickEventArgs e2 = new TreeNodeMouseClickEventArgs(newNode, MouseButtons.Left, 1, 0, 0);

                        this.tvSiteMap.SelectedNode.Nodes.Add(newNode);
                        this.tvSiteMap_NodeMouseClick(this.tvSiteMap, e2);
                    }
                }
            }
            else if (e.ClickedItem.Text == "Delete")
            {
                this.tvSiteMap.SelectedNode.Remove();
            }
            else if (e.ClickedItem.Text == "Disable")
            {
                ((Dictionary<string,string>)this.tvSiteMap.SelectedNode.Tag).Add("_disabled","true");
                tvSiteMap.SelectedNode.ForeColor = Color.Gray;
                tvSiteMap.SelectedNode.Text += " - disabled";
                tvSiteMap.SelectedNode.ToolTipText =
                    "This node is disabled and won't appear in Microsoft Dynamics CRM 2011. Right click this node and enable it and make it appear on Microsoft Dynamics CRM 2011";
                
            }
            else if (e.ClickedItem.Text == "Enable")
            {
                ((Dictionary<string, string>)this.tvSiteMap.SelectedNode.Tag).Remove("_disabled");
                tvSiteMap.SelectedNode.ForeColor = Color.Black;
                tvSiteMap.SelectedNode.Text = tvSiteMap.SelectedNode.Text.Replace(" - disabled", "");
                tvSiteMap.SelectedNode.ToolTipText = null;
            }
            else if (e.ClickedItem.Text == "Cut" || e.ClickedItem.Text == "Copy" || e.ClickedItem.Text == "Paste")
            {
                if (e.ClickedItem.Text == "Cut")
                    this.clipboard.Cut(this.tvSiteMap.SelectedNode);
                else if (e.ClickedItem.Text == "Copy")
                    this.clipboard.Copy(this.tvSiteMap.SelectedNode);
                else
                    this.clipboard.Paste(this.tvSiteMap.SelectedNode);
            }
            else
            {
                string nodeText = e.ClickedItem.Name.Remove(0, 3);
                nodeText = nodeText.Substring(0, nodeText.IndexOf("ToolStripMenuItem"));

                TreeNode newNode = new TreeNode(nodeText);
                newNode.Tag = new Dictionary<string, string>();
                newNode.Name = newNode.Text.Replace(" ", "");

                if (newNode.Text == "Descriptions" || newNode.Text == "Titles")
                {
                    TreeNode newSubNode = new TreeNode(newNode.Text.Remove(newNode.Text.Length - 1, 1));
                    newSubNode.Name = newSubNode.Text.Replace(" ", "");
                    newNode.Nodes.Add(newSubNode);

                    TreeNodeMouseClickEventArgs e2 = new TreeNodeMouseClickEventArgs(newSubNode, MouseButtons.Left, 1, 0, 0);
                    this.tvSiteMap.SelectedNode.Nodes.Add(newNode);
                    this.tvSiteMap_NodeMouseClick(this.tvSiteMap, e2);
                }
                else
                {
                    TreeNodeMouseClickEventArgs e2 = new TreeNodeMouseClickEventArgs(newNode, MouseButtons.Left, 1, 0, 0);
                    this.tvSiteMap.SelectedNode.Nodes.Add(newNode);
                    this.tvSiteMap_NodeMouseClick(this.tvSiteMap, e2);
                }
            }
        }

        #endregion ContextMenu Handlers

        #region Load SiteMap Methods

        private void LoadSiteMap()
        {
            CommonDelegates.SetCursor(this, Cursors.WaitCursor);
            this.ccsb.SetMessage("Loading SiteMap...");
            this.EnableControls(false);

            BackgroundWorker loadSiteMapWorker = new BackgroundWorker();
            loadSiteMapWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(loadSiteMapWorker_RunWorkerCompleted);
            loadSiteMapWorker.DoWork += new DoWorkEventHandler(loadSiteMapWorker_DoWork);
            loadSiteMapWorker.RunWorkerAsync();
        }

        void loadSiteMapWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            QueryExpression qe = new QueryExpression("sitemap");
            qe.ColumnSet = new ColumnSet(true);

            EntityCollection ec = service.RetrieveMultiple(qe);

            this.siteMap = ec[0];
            this.siteMapDoc = new XmlDocument();
            this.siteMapDoc.LoadXml(ec[0]["sitemapxml"].ToString());

            this.DisplaySiteMap();

            CommonDelegates.SetCursor(this, Cursors.Default);
            this.ccsb.SetMessage("");
            this.EnableControls(true);
        }

        void loadSiteMapWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            CommonDelegates.SetCursor(this, Cursors.Default);
            this.ccsb.SetMessage("");
            this.EnableControls(true);
        }

        #endregion Load SiteMap Methods

        #region Update SiteMap Methods

        private void UpdateSiteMap()
        {
            if (this.siteMap == null)
            {
                QueryExpression qe = new QueryExpression("sitemap");
                qe.ColumnSet = new ColumnSet(true);

                EntityCollection ec = service.RetrieveMultiple(qe);

                this.siteMap = ec[0];
                this.siteMapDoc = new XmlDocument();
                this.siteMapDoc.LoadXml(ec[0]["sitemapxml"].ToString());
            }

            CommonDelegates.SetCursor(this, Cursors.WaitCursor);
            this.EnableControls(false);
            this.ccsb.SetMessage("Updating Sitemap...");

            BackgroundWorker updateWorker = new BackgroundWorker();
            updateWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateWorker_RunWorkerCompleted);
            updateWorker.DoWork += new DoWorkEventHandler(updateWorker_DoWork);
            updateWorker.RunWorkerAsync();
        }

        void updateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Build the Xml SiteMap from SiteMap TreeView
            XmlDocument doc = new XmlDocument();
            XmlNode rootNode = doc.CreateElement("SiteMap");
            doc.AppendChild(rootNode);

            this.AddXmlNode(this.tvSiteMap.Nodes[0], rootNode);

            this.siteMap["sitemapxml"] = doc.SelectSingleNode("SiteMap/SiteMap").OuterXml;
            this.siteMapDoc.LoadXml(doc.SelectSingleNode("SiteMap/SiteMap").OuterXml);

            service.Update(this.siteMap);

            PublishXmlRequest request = new PublishXmlRequest();
            request.ParameterXml = "<importexportxml><sitemaps><sitemap></sitemap></sitemaps></importexportxml>";
            service.Execute(request);
        }

        void updateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                CommonDelegates.DisplayMessageBox(this, "Error while updating SiteMap: " + e.Error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.ccsb.SetMessage("Error!");
            }
            else
            {
                this.ccsb.SetMessage("SiteMap updated!");
            }

            CommonDelegates.SetCursor(this, Cursors.Default);
            this.EnableControls(true);
        }

        #endregion Update SiteMap Methods

        #region Others

        /// <summary>
        /// Loads the SiteMap from the extracted Xml solution files
        /// </summary>
        private void DisplaySiteMap()
        {
            XmlNode siteMapXmlNode = null;

            MethodInvoker miReadSiteMap = delegate
            {
                siteMapXmlNode = this.siteMapDoc.DocumentElement;
            };

            if (this.InvokeRequired)
                this.Invoke(miReadSiteMap);
            else
                miReadSiteMap();

            MethodInvoker miFillTreeView = delegate
            {
                this.tvSiteMap.Nodes.Clear();

                TreeNodeHelper.AddTreeViewNode(this.tvSiteMap, siteMapXmlNode, this);

                this.ManageMenuDisplay();
                this.tvSiteMap.Nodes[0].Expand();
            };

            if (this.tvSiteMap.InvokeRequired)
            {
                this.tvSiteMap.Invoke(miFillTreeView);
            }
            else
            {
                miFillTreeView();
            }
        }

        /// <summary>
        /// When SiteMap component properties are saved, they are
        /// copied in the current selected TreeNode
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ctrl_Saved(object sender, SaveEventArgs e)
        {
            this.tvSiteMap.SelectedNode.Tag = e.AttributeCollection;
        }

        /// <summary>
        /// Manages which controls should be visible/enabled
        /// </summary>
        private void ManageMenuDisplay()
        {
            TreeNode selectedNode = this.tvSiteMap.SelectedNode;

            this.tsbItemSave.Enabled = selectedNode != null;
            this.toolStripButtonDelete.Enabled = selectedNode != null && selectedNode.Text != "SiteMap";
            this.toolStripButtonMoveUp.Enabled = selectedNode != null && selectedNode.Parent != null && selectedNode.Index != 0;
            this.toolStripButtonMoveDown.Enabled = selectedNode != null && selectedNode.Parent != null && selectedNode.Index != selectedNode.Parent.Nodes.Count - 1;
            this.toolStripButtonAddXml.Enabled = selectedNode != null && selectedNode.Text != "Title" && selectedNode.Text != "Description" && selectedNode.Text != "Privilege";
            this.toolStripButtonDisplayXml.Enabled = selectedNode != null;

            this.toolStripDropDownButtonMoreActions.Enabled = this.tvSiteMap.Nodes.Count > 0;
            this.tsbUpdateSiteMap.Enabled = this.tvSiteMap.Nodes.Count > 0;
            this.toolStripButtonSaveSiteMapToDisk.Enabled = this.tvSiteMap.Nodes.Count > 0;
        }

        /// <summary>
        /// Add the specified TreeNode properties in a XmlNode
        /// </summary>
        /// <param name="currentNode">TreeNode to add to the Xml</param>
        /// <param name="parentXmlNode">XmlNode where to add data</param>
        private void AddXmlNode(TreeNode currentNode, XmlNode parentXmlNode)
        {
            XmlNode newNode = parentXmlNode.OwnerDocument.CreateElement(currentNode.Text.Split(' ')[0]);

            Dictionary<string, string> collec = (Dictionary<string, string>)currentNode.Tag;

            foreach (string key in collec.Keys)
            {
                if (key != "_disabled")
                {
                    XmlAttribute attr = parentXmlNode.OwnerDocument.CreateAttribute(key);
                    attr.Value = collec[key];

                    newNode.Attributes.Append(attr);
                }
            }

            TreeNode titles = null;
            TreeNode descriptions = null;
            List<TreeNode> others = new List<TreeNode>();

            foreach (TreeNode childNode in currentNode.Nodes)
            {
                if (childNode.Text == "Titles")
                    titles = childNode;
                else if (childNode.Text == "Descriptions")
                    descriptions = childNode;
                else
                    others.Add(childNode);
            }

            if (titles != null)
                this.AddXmlNode(titles, newNode);
            if (descriptions != null)
                this.AddXmlNode(descriptions, newNode);
            foreach (TreeNode otherNode in others)
                this.AddXmlNode(otherNode, newNode);

            if(collec.ContainsKey("_disabled"))
            {
                var comment = parentXmlNode.OwnerDocument.CreateComment(newNode.OuterXml);
                parentXmlNode.AppendChild(comment);
            }
            else
            {
                parentXmlNode.AppendChild(newNode);
            }
        }

        /// <summary>
        /// Enables or disables specific controls
        /// </summary>
        /// <param name="enabled">Flag that indicates if controls must be enabled</param>
        internal void EnableControls(bool enabled)
        {
            MethodInvoker mi = delegate
            {
                this.tsbMainOpenSiteMap.Enabled = enabled;
                this.tsbUpdateSiteMap.Enabled = enabled;
                this.toolStripButtonSaveSiteMapToDisk.Enabled = enabled;
                this.toolStripButtonLoadSiteMapFromDisk.Enabled = enabled;
                this.toolStripDropDownButtonMoreActions.Enabled = enabled;
                this.gbSiteMap.Enabled = enabled;
                this.gbProperties.Enabled = enabled;
            };

            if (this.InvokeRequired)
            {
                this.Invoke(mi);
            }
            else
            {
                mi();
            }
        }

        #endregion Others

        private void tsbRate_Click(object sender, EventArgs e)
        {
            Process.Start("http://sitemapeditor.codeplex.com/releases");
        }

        private void tsbDiscuss_Click(object sender, EventArgs e)
        {
            Process.Start("http://sitemapeditor.codeplex.com/discussions");
        }

        private void tsbReportBug_Click(object sender, EventArgs e)
        {
            Process.Start("http://sitemapeditor.codeplex.com/WorkItem/Create");
        }

        #endregion Methods

        private void tsbDonate_Click(object sender, EventArgs e)
        {
            string url = "";

            string business = "tanguy92@hotmail.com";  // your paypal email
            string description = "Donation%20for%20MSCRM%20Tools";            // '%20' represents a space. remember HTML!
            string country = "FR";                  // AU, US, etc.
            string currency = "EUR";                 // AUD, USD, etc.

            url += "https://www.paypal.com/cgi-bin/webscr" +
                "?cmd=" + "_donations" +
                "&business=" + business +
                "&lc=" + country +
                "&item_name=" + description +
                "&currency_code=" + currency +
                "&bn=" + "PP%2dDonationsBF";

            System.Diagnostics.Process.Start(url);

        }

        private void tsbDefineProxySettings_Click(object sender, EventArgs e)
        {
            this.cManager.EditProxySettings(this);
        }
    }
}
